package org.entities;

import org.GameState;
import org.BlockMap;
import org.Progress;
import org.StatusState;
import org.entities.Elevator;
import org.newdawn.slick.Animation;
import org.newdawn.slick.Image;
import org.newdawn.slick.Sound;
import org.newdawn.slick.SpriteSheet;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Color;
public class Player extends LevelObject {

    public static final float MOVE_SPEED = 0.1f;          // player velocity
    private static int size[] = {10, 0, 38, 0, 38, 48, 10, 48};   // player dimensions
    private float dx = 0;
    private float dy = 0;
    public LevelObject pBox;
    private int currentShootTime = 0;    
    private int maxShootTime = 800;      
    private boolean nextShootOK;         
    private int currentJumpTime = 0;     
    private int maxJumpTime = 1000;
    private int currentWalkTime = 0;     
    private int maxWalkTime = 800;
    private boolean somersaultRight,somersaultLeft;
    private boolean wallJumpLeft, wallJumpRight;
    private boolean jReleased;
    private boolean jCtrlLeft,jCtrlRight,jReleasedCtrl;
    private boolean slope;

    private boolean nextWalkOK=true;
    //private boolean falling;

    public static boolean 
    appearLeftDown, 
    appearRightDown,
    appearRightUp,
    appearLeftUp,
    doorLD,
    doorRD,
    doorLU,
    doorRU;

    public Animation 
    playerGoingLeft, 
    playerGoingRight, 
    playerStandLookingLeft,
    playerStandLookingRight,
    playerDefault,
    playerJumpingLeft,
    playerJumpingRight,
    playerFallingLeft,
    playerFallingRight,
    playerShootingLeft,
    playerShootingRight,
    fallback;
    //public String lastFacingDirection;
    //public boolean facingLeft, facingRight;
    public static boolean Freeze;
    private Sound baseShot;
    private Sound stepSound;
    private Sound landing;
    private Sound spinning;
    //private boolean applyGravity;
    private boolean landSoundCtrl;
    private boolean spinSoundCtrl;
    private boolean shootCtrl;
    public Image playerShoot;



    public Player(float x, float y, String type) throws SlickException {

        // ==========================
        // Create player bounding box
        // ==========================
        super(x, y,               
                (int)x + size[0], (int)y + size[1],
                (int)x + size[2], (int)y + size[3],
                (int)x + size[4], (int)y + size[5],
                (int)x + size[6], (int)y + size[7],
                type);

        // ========================
        // load player sprite sheet
        // ========================

        SpriteSheet sheet = new SpriteSheet("data/anim/samus.png", 48, 48);

        // ====================
        // create player sprite
        // ====================
        playerGoingLeft = new Animation();
        for (int frame = 0; frame < 10; frame++) {
            playerGoingLeft.addFrame(sheet.getSprite(frame, 0), 180);
        }
        playerGoingRight = new Animation();
        for (int frame = 0; frame < 10; frame++) {
            playerGoingRight.addFrame(sheet.getSprite(frame, 1), 180);
        }
        playerStandLookingLeft = new Animation();
        for (int frame = 0; frame < 4; frame++) {
            playerStandLookingLeft.addFrame(sheet.getSprite(frame, 2), 1080);
        }
        playerStandLookingRight = new Animation();
        for (int frame = 0; frame < 4; frame++) {
            playerStandLookingRight.addFrame(sheet.getSprite(frame, 3), 1080);
        }
        playerJumpingRight = new Animation();
        for (int frame = 0; frame < 8; frame++) {
            playerJumpingRight.addFrame(sheet.getSprite(frame, 10), 180);
        }
        playerJumpingLeft = new Animation();
        for (int frame = 0; frame < 8; frame++) {
            playerJumpingLeft.addFrame(sheet.getSprite(frame, 9), 180);
        }
        playerFallingLeft = new Animation();
        for (int frame = 0; frame < 8; frame++) {
            playerFallingLeft.addFrame(sheet.getSprite(frame, 4), 250);
        }
        playerFallingRight = new Animation();
        for (int frame = 0; frame < 8; frame++) {
            playerFallingRight.addFrame(sheet.getSprite(frame, 5), 250);
        }
        playerShootingLeft = new Animation();
        for (int frame = 0; frame < 10; frame++) {
            playerShootingLeft.addFrame(sheet.getSprite(frame, 11), 180);
        }
        playerShootingRight = new Animation();
        for (int frame = 0; frame < 10; frame++) {
            playerShootingRight.addFrame(sheet.getSprite(frame, 12), 180);
        }
        playerDefault = new Animation();
        for (int frame = 0; frame < 2; frame++) {
            playerDefault.addFrame(sheet.getSprite(frame, 6), 180);
        }

        playerGoingLeft.setAutoUpdate(false);
        playerGoingRight.setAutoUpdate(false);
        playerJumpingLeft.setAutoUpdate(false);
        playerJumpingRight.setAutoUpdate(false);
        playerStandLookingLeft.setAutoUpdate(false);
        playerStandLookingRight.setAutoUpdate(false);
        playerFallingLeft.setAutoUpdate(false);
        playerFallingRight.setAutoUpdate(false);
        playerDefault.setAutoUpdate(false);
        playerShootingLeft.setAutoUpdate(false);
        playerShootingRight.setAutoUpdate(false);
        fallback = playerDefault;

        // ===================
        // load some resources
        // ===================
        baseShot = new Sound("data/sound/BaseShot2.ogg");
        stepSound = new Sound("data/sound/Step2.ogg");
        landing = new Sound("data/sound/Land.ogg");
        spinning = new Sound("data/sound/Spin2.ogg");
        playerShoot=new Image("data/anim/bullet.png");
    }

    public void update(int delta) throws SlickException {       

        // ================
        // taking damage
        // ================
        if (gotHurt){ 
            //entity.addShields(-5);
            if (Progress.shields>0)
                Progress.shields-=10;
            else
                Progress.health-=20;
            System.out.printf("progress shields=[%d]\n",Progress.shields);
            gotHurt=false;
        }



        // ===================
        // movement control
        // =================== 
        if (Freeze){
            fallback=playerDefault;
            playerDefault. update(delta);  
        }

        if (!Freeze){
            if (GameState.up){moveUp=true;}else{moveUp=false;}
            if (GameState.down){moveDown=true;}else{moveDown=false;}
            if (GameState.left){moveLeft=true;}else{moveLeft=false;}
            if (GameState.right){moveRight=true;}else{moveRight=false;}
            if (GameState.action1){action=true;}else{action=false;}
            if (GameState.shoot){shoot=true;}else{shoot=false; shootCtrl=false;}
            if (GameState.boost){boost=true;}else{boost=false;}
            if (GameState.jump){

             //   
             //   
             //   finish wall jump
             //   
             //  
                if (jReleased&&!jReleasedCtrl){
                    jReleasedCtrl=true;
                    //jReleased=true;
                }
                
                if  (currentJumpTime<maxJumpTime){
                    currentJumpTime += delta;
                    jump=true;
                }

                if (currentJumpTime >= maxJumpTime) {                    
                    jump=false;
                }                                
            }           

            if (GameState.jumpRelease){
                jReleased=true;
                jump=false; 
            }

            if (boost){
                dx = (float) delta * MOVE_SPEED*2;
                dy = (float) delta * MOVE_SPEED;
            }else{
                dx = (float) delta * MOVE_SPEED;
                dy = (float) delta * MOVE_SPEED;  
            }        

            // ==================
            // left,right,up,down
            // ==================
            if (moveDown){
                y += dy;
                poly.setY(y);
                if (GameState.entityCollisionWith(this)){
                    y -= dy;
                    poly.setY(y);
                }
            }

            if (moveLeft){
                if (!stepSound.playing()){
                    walking(delta);
                }
                x -= dx;
                poly.setX(x + size[0]);
                if (GameState.entityCollisionWith(this)){
                    if (diagDown){
                        y-=(getVector().getY());                
                        poly.setY(y);
                    }else{
                        x += dx;
                        poly.setX(x + size[0]);
                    }
                }
            }

            if (moveRight){
                if (!stepSound.playing()){
                    walking(delta);
                }
                x += dx;
                poly.setX(x + size[0]);
                if (GameState.entityCollisionWith(this)){
                    if (diagUp){
                        y-=(getVector().getY());                
                        poly.setY(y);
                    }else{
                        x -= dx;
                        poly.setX(x + size[0]);
                    }
                }
            }
            // ================
            // jump, walljump 
            // ================
            if (jump){
                landSoundCtrl=false;
                y -= dy*2;
                poly.setY(y);
                if (GameState.entityCollisionWith(this)){
                    y += dy*2 ;
                    poly.setY(y);                    
                }                
            }

            if (wallJumpLeft&&!onGround&&jReleased){
                if (jump&&!jCtrlLeft){
                    jCtrlRight=false;
                    jCtrlLeft=true;
                    jReleased=false;
                    currentJumpTime=0;
                }
            }
            
            if (wallJumpRight&&!onGround&&jReleased){
                if (jump&&!jCtrlRight){
                    jCtrlRight=true;
                    jCtrlLeft=false;                    
                    jReleased=false;
                    currentJumpTime=0;
                }
            }
             
            // ================
            // gravity system
            // ================

            if (!GameState.entityCollisionWith(this)){
                applyGravity=true;
                if (!jump)
                falling=true;                
            }            

            if (applyGravity){//&&!diagUp&&!diagDown){
                y += dy;
                poly.setY(y);
                if (GameState.entityCollisionWith(this)){
                    falling=false;
                    y -= dy;
                    poly.setY(y);
                    applyGravity=false;
                }                
            }            

            if (onGround){
                spinSoundCtrl=true;
                somersaultLeft=false;
                somersaultRight=false;
                jCtrlLeft=false;      
                jCtrlRight=false;
                
                if (jReleased){
                    currentJumpTime=0;
                    jReleased=false;
                }
                
                if (!landSoundCtrl){ 
                    landSoundCtrl=true;                        
                    landing.play(1,Progress.sfxVol/20);
                }
            }

            if (!onGround){
                spinSoundCtrl=false;
                if (somersaultLeft||somersaultRight){
                    if (!spinning.playing()&&!spinSoundCtrl)
                        spinning.play(1,Progress.sfxVol/20);
                }
            }

            // ========
                // attack
            // ========

            if (shoot&&!(moveLeft&&moveRight)) {
                // include a message on the status screen
                // detail for boost: no shoot while active.
                if (lastFacingDirection!=null&&!boost) 
                    attack(x, y, delta);
            }



            // ==============================
            // select player animation
            // ==============================              

            // =========
            // case run
            // =========            
            if (moveLeft&&!shoot&&!jump){
                //System.out.println("test me 0a [Run left]");
                lastFacingDirection="left";
                fallback = playerGoingLeft;
                playerGoingLeft.update(delta);
            }
            if (moveRight&&!shoot&&!jump){
                //System.out.println("test me 0b [Run right]");
                lastFacingDirection="right";
                fallback = playerGoingRight;
                playerGoingRight.update(delta);
            }

            // ==================
            // case run and shoot
            // ==================
            if (moveLeft&&shoot&&!jump&&!moveRight) {
                //System.out.println("test me 3 [Run left and shoot]");
                lastFacingDirection="left";
                fallback = playerShootingLeft;
                playerShootingLeft.update(delta);              
            }
            if (moveRight&&shoot&&!jump&&!moveLeft) {
                //System.out.println("test me 4 [Run right and shoot]");
                lastFacingDirection="right";
                fallback = playerShootingRight;
                playerShootingRight.update(delta);
            }

            // =================
            // case run and jump
            // =================
            if (jump&&moveLeft&&!shoot&&!moveRight) {              
                somersaultLeft=true;
                somersaultRight=false;
                //fallback = playerJumpingLeft;
                //playerJumpingLeft. update(delta);
            }            
            if (jump&&moveRight&&!shoot&&!moveLeft) {
                somersaultRight=true;
                somersaultLeft=false;
                //fallback = playerJumpingRight;
                //playerJumpingRight. update(delta);
            }
            if (somersaultLeft){
                if (!onGround){
                    fallback = playerJumpingLeft;
                    playerJumpingLeft. update(delta); 
                }
            }
            if (somersaultRight){
                if (!onGround){
                    fallback = playerJumpingRight;
                    playerJumpingRight. update(delta);  
                }
            }


            if (lastFacingDirection!=null){                
                // ==============
                // case standing onGround&&
                // ==============
                if (lastFacingDirection.equals("left")&&!moveRight&&!moveLeft&&!shoot&&!jump){
                    //System.out.println("test me 5 [Stand looking Left]");
                    fallback = playerStandLookingLeft;
                    playerStandLookingLeft.update(delta);
                }
                if (lastFacingDirection.equals("right")&&!moveRight&&!moveLeft&&!shoot&&!jump){
                    //System.out.println("test me 6 [Stand looking right]");
                    fallback = playerStandLookingRight; 
                    playerStandLookingRight.update(delta);
                }                
                // ====================
                // case stand and shoot
                // ====================
                if (lastFacingDirection.equals("left")&&shoot&&!moveLeft&&!moveRight&&!jump) {                    
                    //System.out.println("test me 1 [Stand looking left and shoot]");
                    fallback = playerStandLookingLeft;
                    playerStandLookingLeft.update(delta);
                }
                if (lastFacingDirection.equals("right")&&shoot&&!moveLeft&&!moveRight&&!jump) {
                    //System.out.println("test me 2 [Stand looking right and shoot]");
                    fallback = playerStandLookingRight;
                    playerStandLookingRight.update(delta);
                }                
                // ====================
                // case stand and jump
                // ====================
                if (jump&&lastFacingDirection.equals("left")&&!shoot&&!moveLeft&&!moveRight) {                    
                    //System.out.println("test me 1 [Stand looking left and shoot]");
                    fallback =  playerFallingLeft;
                    playerFallingLeft. update(delta);
                }
                if (jump&&lastFacingDirection.equals("right")&&!shoot&&!moveLeft&&!moveRight) {
                    //System.out.println("test me 2 [Stand looking right and shoot]");
                    fallback =  playerFallingRight;
                    playerFallingRight. update(delta);
                }
                // =============
                // case: falling
                // =============
                if (falling&&lastFacingDirection.equals("left")&&!jump&&!slope){
                    fallback =  playerFallingLeft;
                    playerFallingLeft. update(delta);
                }
                if (falling&&lastFacingDirection.equals("right")&&!jump&&!slope){
                    fallback =  playerFallingRight;
                    playerFallingRight. update(delta);
                }

                // =============
                // case: errors
                // =============
                if (moveLeft&&moveRight&&!falling){
                    fallback=playerDefault;
                    playerDefault. update(delta);
                } 
                
            }


            // ===================
            // attack timing
            // ===================
            if (currentShootTime < maxShootTime) {
                currentShootTime += delta;
                nextShootOK = false;
            }
            if (currentShootTime >= maxShootTime) {
                shootCtrl=false;
                nextShootOK = true;
            }
        }

        // ==============================
        // player don't cross map borders
        // ==============================        
        if (x < 0) {
            x = 0;
        }
        if (y < 0) {
            y = 0;
        }
        if (x > BlockMap.mapWidth - 48) {
            x = BlockMap.mapWidth - 48;
        }
        if (y > BlockMap.mapHeight - 50) {
            y = BlockMap.mapHeight - 50;
        }
        if (GameState.restartGame) {
            setShields(MAX_SHIELDS);
        }
        //System.out.printf("onGround=[%b]\n",onGround);
        //System.out.printf("somersaultLeft=[%b]\n",somersaultLeft);
        //System.out.printf("somersaultRight=[%b]\n",somersaultRight);
        //System.out.printf("wallJumpRight=[%b]\n",wallJumpRight);
        //System.out.printf("wallJumpLeft=[%b]\n",wallJumpLeft);
    }

    public void walking(long delta){
        if (currentWalkTime < maxWalkTime) {
            if (boost)
                currentWalkTime += delta*2;
            else
                currentWalkTime += delta;  
        }
        if (nextWalkOK&&!jump&&!applyGravity) {
            if (Progress.strength<=0){
                Progress.health-=0.4;
            }
            else{
                if (boost){
                    //System.out.println("Boost fatigue");
                    Progress.strength-=0.6;
                }
                else{
                    //System.out.println("No boost fatigue");
                    Progress.strength-=0.3;
                }
            }
            stepSound.play(1,Progress.sfxVol/20);
        }
        if (currentWalkTime >= maxWalkTime) {
            nextWalkOK = true;
            currentWalkTime = 0;
        } else {
            nextWalkOK = false;
        }
    }   


    public void attack(float x, float y, long delta) throws SlickException {

        //if (){
        if (nextShootOK||!shootCtrl) {            
            if (lastFacingDirection.equals("left")) {
                BlockMap.entities.add(new Shoot((int)x-5,(int)y+15, playerShoot,"left","bullet"));
                baseShot.play(1,Progress.sfxVol/20);
            }
            if (lastFacingDirection.equals("right")) {
                BlockMap.entities.add(new Shoot((int)x+40,(int)y+15, playerShoot,"right","bullet"));
                baseShot.play(1,Progress.sfxVol/20);
            }
            Progress.energy-=0.5;
            shootCtrl=true;
            currentShootTime = 0;
        }
        //   }
    }

    public boolean isCollisionWith(LevelObject other) throws SlickException {

       
        if (BlockMap.getBlockAt(((int)x+26)/16,((int)y+48)/16)!=null){
            onGround=true;    
        }else{onGround=false;}
        
        
        if (BlockMap.getBlockAt(((int)x+26)/16,((int)y+60)/16)!=null){
            slope=true;
        }else{slope=false;}
        
        
        if (BlockMap.getBlockAt(((int)x)/16,((int)y+24)/16)!=null){
            wallJumpLeft=true;            
        }else{wallJumpLeft=false;}
        if (BlockMap.getBlockAt(((int)x+48)/16,((int)y+24)/16)!=null){            
            wallJumpRight=true;
        }else{wallJumpRight=false;}

        if (other.type.equals("elevStop")){
            if (poly.contains(other.poly.getX(),other.poly.getY())||poly.intersects(other.poly)){
                //System.out.println("intersect with elevstop");
                if (moveDown&&!Elevator.elevExist){
                    BlockMap.entities.add(new Elevator((int)other.x, (int)other.y, "elev"));
                    Elevator.moveDown=true;
                }
            }
            return false;
        }
        if (other.type.equals("elevStart")){

            if (poly.contains(other.poly.getX(),other.poly.getY())||poly.intersects(other.poly)){
                if (moveUp&&!Elevator.elevExist){
                    BlockMap.entities.add(new Elevator((int)other.x, (int)other.y, "elev"));
                    Elevator.moveUp=true;
                }
            }
            return false;
        }

        if (other.type.equals("doorLD")){
            if (poly.intersects(other.poly)){
                String test = BlockMap.tmap.getTileProperty(8, "appearRightDown", "nothing");
                if (test.equals("true")){ appearRightDown = true;  appearRightUp = false; }
                else{ appearRightDown = false;  appearRightUp = true; }
                appearLeftDown = false;
                appearLeftUp = false;
                doorLD=true;
                GameState.transition=true;
            }
        }
        if (other.type.equals("doorRD")){
            if (poly.intersects(other.poly)){
                String test = BlockMap.tmap.getTileProperty(9, "appearLeftDown", "nothing");
                if (test.equals("true")){ appearLeftDown = true;  appearLeftUp = false; }
                else{ appearLeftDown = false;  appearLeftUp = true; }
                appearRightDown = false;
                appearRightUp = false;
                doorRD=true;
                GameState.transition=true;
            }
        }
        if (other.type.equals("doorLU")){
            if (poly.intersects(other.poly)){
                String test = BlockMap.tmap.getTileProperty(10, "appearRightDown", "nothing");
                if (test.equals("true")){ appearRightDown = true;  appearRightUp = false; }
                else{ appearRightDown = false;  appearRightUp = true; }
                appearLeftDown = false;
                appearLeftUp = false;
                doorLU=true;
                GameState.transition=true;

            }
        }

        if (other.type.equals("doorRU")){
            if (poly.intersects(other.poly)){
                String test = BlockMap.tmap.getTileProperty(11, "appearLeftDown", "nothing");
                if (test.equals("true")){ appearLeftDown = true;  appearLeftUp = false; }
                else{ appearLeftDown = false;  appearLeftUp = true; }
                appearRightDown = false;
                appearRightUp = false;
                doorRU=true;
                GameState.transition=true;
            }
        }

        if (other.type.equals("dDown")){
            if (poly.intersects(other.poly)){
                if (GameState.left){
                    diagDown=true;  
                }
            }else{diagDown=false;}  
        }
        if (other.type.equals("dUp")){            
            if (poly.intersects(other.poly)){
                if (GameState.right){
                    diagUp=true;    
                }
            }else{diagUp=false;}   
        }

        if (other.type.equals("marine")){
            if (poly.intersects(other.poly)){
                return false;
            }
        }
        if (other instanceof Sas){
            if (poly.intersects(other.poly)){
                return false;
            }
        }

        if (other instanceof Item){
            if (poly.intersects(other.poly)){
                StatusState.RegisterItem(other.type);
                other.gotHurt=true;
                return false;
            }
        }


        return poly.intersects(other.poly);
    }



    // =====================
    // player render code
    // =====================
    public void draw(Graphics g) {
        g.setColor(Color.white);
        g.drawAnimation(fallback, x, y);
        if (debugMode)
            g.draw(poly);
    }
}


